#include "LuaLink.h"
#include "LuaLink_SceneNode.h"
#include "LuaLink_Entity.h"

LuaLink::LuaLink() {
	sceneManager = NULL;
	gui = NULL;
	camera = NULL;
	trace = NULL;
	state = lua_open();
	running = true;
}

LuaLink::~LuaLink(void) {
	if (sceneManager != NULL) {
		delete sceneManager;
	}
	if (trace != NULL) {
		delete trace;
	}
	if (gui != NULL) {
		delete gui;
	}
	if (camera != NULL) {
		delete camera;
	}

	//Cuando C++ contiene objetos de Lua
	//La siguiente funcion marca error...
	//Al parecer destruimos objetos antes de tiempo
	//Hay que ver la manera de destruirlos adecuadamente
	//FIXME: Arreglar el problema!
	//lua_close(state);
	state = NULL;
}

bool LuaLink::doFile(const Ogre::String& file) {
	try {
		bool resultado = luaL_dofile(state, file.c_str());
		if (resultado) {
			lua_trace("ERROR EN EL SCRIPT");
			return false;
		} else {
			return true;
		}
	} catch (luabind::error& error) {
		lua_trace("LUA ERROR ON doFile:");
		lua_trace(error.what());
	}

	return false;
}

void LuaLink::bind(void) {
	luaL_openlibs(state);
	luabind::open(state);

	module(state) [
		class_<Mouse>("Mouse")
			.enum_("constants") [
				value("Left", OIS::MB_Left),
				value("Right,", OIS::MB_Right),
				value("Middle,", OIS::MB_Middle),
				value("Button3,", OIS::MB_Button3),
				value("Button4,", OIS::MB_Button4),
				value("Button5,", OIS::MB_Button5),
				value("Button6,", OIS::MB_Button6),
				value("Button7", OIS::MB_Button7)
			],
		class_<Key>("Key")
			.enum_("constants") [
				value("KC_UNASSIGNED", OIS::KC_UNASSIGNED),
				value("KC_ESCAPE", OIS::KC_ESCAPE),
				value("KC_1", OIS::KC_1),
				value("KC_2", OIS::KC_2),
				value("KC_3", OIS::KC_3),
				value("KC_4", OIS::KC_4),
				value("KC_5", OIS::KC_5),
				value("KC_6", OIS::KC_6),
				value("KC_7", OIS::KC_7),
				value("KC_8", OIS::KC_8),
				value("KC_9", OIS::KC_9),
				value("KC_0", OIS::KC_0),
				value("KC_MINUS", OIS::KC_MINUS),
				value("KC_EQUALS", OIS::KC_EQUALS),
				value("KC_BACK", OIS::KC_BACK),
				value("KC_TAB", OIS::KC_TAB),
				value("KC_Q", OIS::KC_Q),
				value("KC_W", OIS::KC_W),
				value("KC_E", OIS::KC_E),
				value("KC_R", OIS::KC_R),
				value("KC_T", OIS::KC_T),
				value("KC_Y", OIS::KC_Y),
				value("KC_U", OIS::KC_U),
				value("KC_I", OIS::KC_I),
				value("KC_O", OIS::KC_O),
				value("KC_P", OIS::KC_P),
				value("KC_LBRACKET", OIS::KC_LBRACKET),
				value("KC_RBRACKET", OIS::KC_RBRACKET),
				value("KC_RETURN", OIS::KC_RETURN),
				value("KC_LCONTROL", OIS::KC_LCONTROL),
				value("KC_A", OIS::KC_A),
				value("KC_S", OIS::KC_S),
				value("KC_D", OIS::KC_D),
				value("KC_F", OIS::KC_F),
				value("KC_G", OIS::KC_G),
				value("KC_H", OIS::KC_H),
				value("KC_J", OIS::KC_J),
				value("KC_K", OIS::KC_K),
				value("KC_L", OIS::KC_L),
				value("KC_SEMICOLON", OIS::KC_SEMICOLON),
				value("KC_APOSTROPHE", OIS::KC_APOSTROPHE),
				value("KC_GRAVE", OIS::KC_GRAVE),
				value("KC_LSHIFT", OIS::KC_LSHIFT),
				value("KC_BACKSLASH", OIS::KC_BACKSLASH),
				value("KC_Z", OIS::KC_Z),
				value("KC_X", OIS::KC_X),
				value("KC_C", OIS::KC_C),
				value("KC_V", OIS::KC_V),
				value("KC_B", OIS::KC_B),
				value("KC_N", OIS::KC_N),
				value("KC_M", OIS::KC_M),
				value("KC_COMMA", OIS::KC_COMMA),
				value("KC_PERIOD", OIS::KC_PERIOD),
				value("KC_SLASH", OIS::KC_SLASH),
				value("KC_RSHIFT", OIS::KC_RSHIFT),
				value("KC_MULTIPLY", OIS::KC_MULTIPLY),
				value("KC_LMENU", OIS::KC_LMENU),
				value("KC_SPACE", OIS::KC_SPACE),
				value("KC_CAPITAL", OIS::KC_CAPITAL),
				value("KC_F1", OIS::KC_F1),
				value("KC_F2", OIS::KC_F2),
				value("KC_F3", OIS::KC_F3),
				value("KC_F4", OIS::KC_F4),
				value("KC_F5", OIS::KC_F5),
				value("KC_F6", OIS::KC_F6),
				value("KC_F7", OIS::KC_F7),
				value("KC_F8", OIS::KC_F8),
				value("KC_F9", OIS::KC_F9),
				value("KC_F10", OIS::KC_F10),
				value("KC_NUMLOCK", OIS::KC_NUMLOCK),
				value("KC_SCROLL", OIS::KC_SCROLL),
				value("KC_NUMPAD7", OIS::KC_NUMPAD7),
				value("KC_NUMPAD8", OIS::KC_NUMPAD8),
				value("KC_NUMPAD9", OIS::KC_NUMPAD9),
				value("KC_SUBTRACT", OIS::KC_SUBTRACT),
				value("KC_NUMPAD4", OIS::KC_NUMPAD4),
				value("KC_NUMPAD5", OIS::KC_NUMPAD5),
				value("KC_NUMPAD6", OIS::KC_NUMPAD6),
				value("KC_ADD", OIS::KC_ADD),
				value("KC_NUMPAD1", OIS::KC_NUMPAD1),
				value("KC_NUMPAD2", OIS::KC_NUMPAD2),
				value("KC_NUMPAD3", OIS::KC_NUMPAD3),
				value("KC_NUMPAD0", OIS::KC_NUMPAD0),
				value("KC_DECIMAL", OIS::KC_DECIMAL),
				value("KC_OEM_102", OIS::KC_OEM_102),
				value("KC_F11", OIS::KC_F11),
				value("KC_F12", OIS::KC_F12),
				value("KC_F13", OIS::KC_F13),
				value("KC_F14", OIS::KC_F14),
				value("KC_F15", OIS::KC_F15),
				value("KC_KANA", OIS::KC_KANA),
				value("KC_ABNT_C1", OIS::KC_ABNT_C1),
				value("KC_CONVERT", OIS::KC_CONVERT),
				value("KC_NOCONVERT", OIS::KC_NOCONVERT),
				value("KC_YEN", OIS::KC_YEN),
				value("KC_ABNT_C2", OIS::KC_ABNT_C2),
				value("KC_NUMPADEQUALS", OIS::KC_NUMPADEQUALS),
				value("KC_PREVTRACK", OIS::KC_PREVTRACK),
				value("KC_AT", OIS::KC_AT),
				value("KC_COLON", OIS::KC_COLON),
				value("KC_UNDERLINE", OIS::KC_UNDERLINE),
				value("KC_KANJI", OIS::KC_KANJI),
				value("KC_STOP", OIS::KC_STOP),
				value("KC_AX", OIS::KC_AX),
				value("KC_UNLABELED", OIS::KC_UNLABELED),
				value("KC_NEXTTRACK", OIS::KC_NEXTTRACK),
				value("KC_NUMPADENTER", OIS::KC_NUMPADENTER),
				value("KC_RCONTROL", OIS::KC_RCONTROL),
				value("KC_MUTE", OIS::KC_MUTE),
				value("KC_CALCULATOR", OIS::KC_CALCULATOR),
				value("KC_PLAYPAUSE", OIS::KC_PLAYPAUSE),
				value("KC_MEDIASTOP", OIS::KC_MEDIASTOP),
				value("KC_VOLUMEDOWN", OIS::KC_VOLUMEDOWN),
				value("KC_VOLUMEUP", OIS::KC_VOLUMEUP),
				value("KC_WEBHOME", OIS::KC_WEBHOME),
				value("KC_NUMPADCOMMA", OIS::KC_NUMPADCOMMA),
				value("KC_DIVIDE", OIS::KC_DIVIDE),
				value("KC_SYSRQ", OIS::KC_SYSRQ),
				value("KC_RMENU", OIS::KC_RMENU),
				value("KC_PAUSE", OIS::KC_PAUSE),
				value("KC_HOME", OIS::KC_HOME),
				value("KC_UP", OIS::KC_UP),
				value("KC_PGUP", OIS::KC_PGUP),
				value("KC_LEFT", OIS::KC_LEFT),
				value("KC_RIGHT", OIS::KC_RIGHT),
				value("KC_END", OIS::KC_END),
				value("KC_DOWN", OIS::KC_DOWN),
				value("KC_PGDOWN", OIS::KC_PGDOWN),
				value("KC_INSERT", OIS::KC_INSERT),
				value("KC_DELETE", OIS::KC_DELETE),
				value("KC_LWIN", OIS::KC_LWIN),
				value("KC_RWIN", OIS::KC_RWIN),
				value("KC_APPS", OIS::KC_APPS),
				value("KC_POWER", OIS::KC_POWER),
				value("KC_SLEEP", OIS::KC_SLEEP),
				value("KC_WAKE", OIS::KC_WAKE),
				value("KC_WEBSEARCH", OIS::KC_WEBSEARCH),
				value("KC_WEBFAVORITES", OIS::KC_WEBFAVORITES),
				value("KC_WEBREFRESH", OIS::KC_WEBREFRESH),
				value("KC_WEBSTOP", OIS::KC_WEBSTOP),
				value("KC_WEBFORWARD", OIS::KC_WEBFORWARD),
				value("KC_WEBBACK", OIS::KC_WEBBACK),
				value("KC_MYCOMPUTER", OIS::KC_MYCOMPUTER),
				value("KC_MAIL", OIS::KC_MAIL),
				value("KC_MEDIASELECT", OIS::KC_MEDIASELECT)
			],

		class_<LuaLink>("Engine")
			.def("trace", &LuaLink::lua_trace)
			.def("debug", &LuaLink::lua_debug)
			.def("getGui", &LuaLink::lua_getGui)
			.def("getCamera", &LuaLink::lua_getCamera)
			.def("getSceneManager", &LuaLink::lua_getSceneManager)
			.def("stop", &LuaLink::lua_stop)
			.def("onEachFrame", &LuaLink::lua_onEachFrame)
			.def("onMouseMoved", &LuaLink::lua_onMouseMoved)
			.def("onMousePressed", &LuaLink::lua_onMousePressed)
			.def("onMouseReleased", &LuaLink::lua_onMouseReleased)
			.def("onKeyPressed", &LuaLink::lua_onKeyPressed)
			.def("onKeyReleased", &LuaLink::lua_onKeyReleased)
		
	];

	luabind::globals(state)["GameEngine"] = this;
}

void LuaLink::bind(Ogre::SceneManager* smgr, Ogre::RenderWindow* window) {
	this->sceneManager = new LuaLink_SceneManager(smgr, window);
	module(state) [
		class_<LuaLink_SceneManager>("SceneManager")
			.def("screenshot", &LuaLink_SceneManager::lua_screenshot)
			.def("setWorldGeometry", &LuaLink_SceneManager::lua_setWorldGeometry),
			
		class_<LuaLink_SceneNode>("SceneNode")
			.def(constructor<const char*>())
			.def(constructor<LuaLink_SceneNode*,const char*>())
			.def("attach", &LuaLink_SceneNode::lua_attach)
			.def("detach", &LuaLink_SceneNode::lua_detach)
			.def("createChild", &LuaLink_SceneNode::lua_createChild)
			.def("position", &LuaLink_SceneNode::lua_position)
			.def("translate", &LuaLink_SceneNode::lua_translate)
			.def("roll", &LuaLink_SceneNode::lua_roll)
			.def("pitch", &LuaLink_SceneNode::lua_pitch)
			.def("yaw", &LuaLink_SceneNode::lua_yaw)
			.def("scale", &LuaLink_SceneNode::lua_scale)
			.def("rotate", &LuaLink_SceneNode::lua_rotate)
			.def("getPosition", &LuaLink_SceneNode::lua_getPosition),

		class_<LuaLink_Entity>("Entity")
			.def("getAnimationCount", &LuaLink_Entity::lua_getAnimationCount)
			.def("getAnimationName", &LuaLink_Entity::lua_getAnimationName)
			.def("animation", &LuaLink_Entity::lua_animation)
			.def("animate", &LuaLink_Entity::lua_animate)
			.def("getAnimationLength", &LuaLink_Entity::lua_getAnimationLength)
			.def("setAnimationTime", &LuaLink_Entity::lua_setAnimationTime)
			.def("getAnimationTime", &LuaLink_Entity::lua_getAnimationTime)
			.def(constructor<const char*, const char*>()),

		class_<Ogre::Vector3>("Vector3")
			.def(constructor<Ogre::Real,Ogre::Real,Ogre::Real>())
			.def(constructor<>())
			.def(constructor<Ogre::Real>())
			.def(const_self == const_self)
			.def(const_self + const_self)
			.def(const_self - const_self)
			.def(const_self * const_self)
			.def(const_self * other<Ogre::Real>())
			.def(const_self / const_self)
			.def(const_self / other<Ogre::Real>())
			.def(const_self < const_self)
			.def("length", &Ogre::Vector3::length)
			.def("distance", &Ogre::Vector3::distance)
			.def("dotProduct", &Ogre::Vector3::dotProduct)
			.def("normalise", &Ogre::Vector3::normalise)
			.def("crossProduct", &Ogre::Vector3::crossProduct)
			.def("midPoint", &Ogre::Vector3::midPoint)
			.def("makeFloor", &Ogre::Vector3::makeFloor)
			.def("makeCeil", &Ogre::Vector3::makeCeil)
			.def("perpendicular", &Ogre::Vector3::perpendicular)
			.def("angleBetwen", &Ogre::Vector3::angleBetween)
			.def("getRotationTo", &Ogre::Vector3::getRotationTo)
			.def("isZeroLength", &Ogre::Vector3::isZeroLength)
			.def("positionEquals", &Ogre::Vector3::positionEquals)
			.def("positionCloses", &Ogre::Vector3::positionCloses)
			.def("directionEquals", &Ogre::Vector3::directionEquals)
			.def_readwrite("x", &Ogre::Vector3::x)
			.def_readwrite("y", &Ogre::Vector3::y)
			.def_readwrite("z", &Ogre::Vector3::z)
	];
}

void LuaLink::bind(MyGUI::Gui *gui) {
	this->gui = new LuaLink_Gui(gui);
	this->trace = new LuaLink_Trace(gui);
	module(state) [
		class_<LuaLink_Gui>("Gui")
			.def("loadLayout", &LuaLink_Gui::lua_loadLayout)
			.def("enable", &LuaLink_Gui::lua_enable)
			.def("disable", &LuaLink_Gui::lua_disable)
			.def("isActive", &LuaLink_Gui::lua_isActive)
			.def("findWidget", &LuaLink_Gui::lua_findWidget),

		class_<LuaLink_Widget>("Widget")
			.def("setPosition", &LuaLink_Widget::lua_setPosition)
			.def("setSize", &LuaLink_Widget::lua_setSize)
			.def("show", &LuaLink_Widget::lua_show)
			.def("hide", &LuaLink_Widget::lua_hide)
			.def("setCaption", &LuaLink_Widget::lua_setCaption)
			.def("getCaption", &LuaLink_Widget::lua_getCaption)
			.def("setAlpha", &LuaLink_Widget::lua_setAlpha)
			.def("getAlpha", &LuaLink_Widget::lua_getAlpha)
			.def("getParent", &LuaLink_Widget::lua_getParent)
			.def("onEventMouseLostFocus", &LuaLink_Widget::lua_onEventMouseLostFocus)
			.def("onEventMouseSetFocus", &LuaLink_Widget::lua_onEventMouseSetFocus)
			.def("onEventMouseDrag", &LuaLink_Widget::lua_onEventMouseDrag)
			.def("onEventMouseMove", &LuaLink_Widget::lua_onEventMouseMove)
			.def("onEventMouseWheel", &LuaLink_Widget::lua_onEventMouseWheel)
			.def("onEventMouseButtonPressed", &LuaLink_Widget::lua_onEventMouseButtonPressed)
			.def("onEventMouseButtonReleased", &LuaLink_Widget::lua_onEventMouseButtonReleased)
			.def("onEventMouseButtonClick", &LuaLink_Widget::lua_onEventMouseButtonClick)
			.def("onEventMouseButtonDoubleClick", &LuaLink_Widget::lua_onEventMouseButtonDoubleClick)
			.def("onEventKeyLostFocus", &LuaLink_Widget::lua_onEventKeyLostFocus)
			.def("onEventKeySetFocus", &LuaLink_Widget::lua_onEventKeySetFocus)
			.def("onEventKeyButtonPressed", &LuaLink_Widget::lua_onEventKeyButtonPressed)
			.def("onEventKeyButtonReleased", &LuaLink_Widget::lua_onEventKeyButtonReleased)
			.def("onEventRootMouseChangeFocus", &LuaLink_Widget::lua_onEventRootMouseChangeFocus)
			.def("onEventRootKeyChangeFocus", &LuaLink_Widget::lua_onEventRootKeyChangeFocus)
			.def("onEventActionInfo", &LuaLink_Widget::lua_onEventActionInfo)
			.def("onRequestGetContainer", &LuaLink_Widget::lua_onRequestGetContainer)
			.def("onEventTooltip", &LuaLink_Widget::lua_onEventToolTip)
	];
}

void LuaLink::bind(Ogre::Camera *camera) {

	this->camera = new LuaLink_Camera(camera);

	module(state) [
		class_<LuaLink_Camera>("Camera")
			.def("setPosition", &LuaLink_Camera::lua_setPosition)
			.def("getPosition", &LuaLink_Camera::lua_getPosition)
			.def("setDirection", &LuaLink_Camera::lua_setDirection)
			.def("getDirection", &LuaLink_Camera::lua_getDirection)
			.def("lookAt", &LuaLink_Camera::lua_lookAt)
			.def("move", &LuaLink_Camera::lua_move)
			.def("moveRelative", &LuaLink_Camera::lua_moveRelative)
			.def("roll", &LuaLink_Camera::lua_roll)
			.def("yaw", &LuaLink_Camera::lua_yaw)
			.def("pitch", &LuaLink_Camera::lua_pitch)
			.def("setNearClipDistance", &LuaLink_Camera::lua_setNearClipDistance)
			.def("setFarClipDistance", &LuaLink_Camera::lua_setFarClipDistance)
	];

	luabind::globals(state)["Camera"] = this->camera;
}

void LuaLink::bind(Ogre::RaySceneQuery *raySceneQuery) {

	this->raySceneQuery = new LuaLink_RaySceneQuery(raySceneQuery);

	module(state) [
		class_<LuaLink_RaySceneQuery>("RaySceneQuery")
			.def("executeWorldIntersection", &LuaLink_RaySceneQuery::lua_executeWorldIntersection)
			.def("getResult", &LuaLink_RaySceneQuery::lua_getResult)
	];

	luabind::globals(state)["RaySceneQuery"] = this->raySceneQuery;
}

void LuaLink::lua_trace(const char* msg) {
	trace->trace(msg);
}

void LuaLink::lua_debug(bool mode) {
	trace->debug(mode);
}

LuaLink_SceneManager* LuaLink::lua_getSceneManager() {
	return sceneManager;
}

LuaLink_Gui* LuaLink::lua_getGui() {
	return gui;
}

LuaLink_Camera* LuaLink::lua_getCamera(){
	return camera;
}

bool LuaLink::isGuiActive() {
	if (gui == NULL) {
		return true;
	}
	return gui->isActive();
}

void LuaLink::lua_stop() {
	running = false;
}

bool LuaLink::isRunning() {
	return running;
}

void LuaLink::eachFrame(const Ogre::FrameEvent& evt) {
	if (luaOnEachFrame) {
		try {
			call_function<void>(luaOnEachFrame, evt.timeSinceLastFrame);
		} catch (luabind::error& error) {
			lua_trace("LUA ERROR ON eachFrame:");
			lua_trace(error.what());
		}
	}
}

void LuaLink::mouseMoved(const OIS::MouseEvent& evt) {
	if (luaOnMouseMoved) {
		try {
			call_function<void>(luaOnMouseMoved, evt.state.X.rel, evt.state.Y.rel, evt.state.Z.rel);
		} catch (luabind::error& error) {
			lua_trace("LUA ERROR ON mouseMoved:");
			lua_trace(error.what());
		}
	}
}

void LuaLink::mousePressed(const OIS::MouseEvent& evt, OIS::MouseButtonID id) {
	if (luaOnMousePressed) {
		try {
			call_function<void>(luaOnMousePressed, id);
		} catch (luabind::error& error) {
			lua_trace("LUA ERROR ON mousePressed:");
			lua_trace(error.what());
		}
	}
}

void LuaLink::mouseReleased(const OIS::MouseEvent& evt, OIS::MouseButtonID id) {
	if (luaOnMouseReleased) {
		try {
			call_function<void>(luaOnMouseReleased, id);
		} catch (luabind::error& error) {
			lua_trace("LUA ERROR ON mouseReleased:");
			lua_trace(error.what());
		}
	}
}

void LuaLink::keyPressed(const OIS::KeyEvent& evt) {
	if (luaOnKeyPressed) {
		try {
			call_function<void>(luaOnKeyPressed, evt.key);
		} catch (luabind::error& error) {
			lua_trace("LUA ERROR ON keyPressed:");
			lua_trace(error.what());
		}
	}
}

void LuaLink::keyReleased(const OIS::KeyEvent& evt) {
	if (luaOnKeyReleased) {
		try {
			call_function<void>(luaOnKeyReleased, evt.key);
		} catch (luabind::error& error) {
			lua_trace("LUA ERROR ON keyReleased:");
			lua_trace(error.what());
		}
	}
}

void LuaLink::lua_onEachFrame(luabind::object obj) {
	luaOnEachFrame = obj;
}

void LuaLink::lua_onMouseMoved(luabind::object obj) {
	luaOnMouseMoved = obj;
}

void LuaLink::lua_onMousePressed(luabind::object obj) {
	luaOnMousePressed = obj;
}

void LuaLink::lua_onMouseReleased(luabind::object obj) {
	luaOnMouseReleased = obj;
}

void LuaLink::lua_onKeyPressed(luabind::object obj) {
	luaOnKeyPressed = obj;
}

void LuaLink::lua_onKeyReleased(luabind::object obj) {
	luaOnKeyReleased = obj;
}
